package com.spzx.order.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.uuid.IdUtils;
import com.spzx.common.core.utils.uuid.UUID;
import com.spzx.common.rabbitmq.SpzxRabbitmqService;
import com.spzx.common.rabbitmq.constants.SpzxRabbitmqConstants;
import com.spzx.common.rabbitmq.domain.SpzxCorrelationData;
import com.spzx.common.security.utils.SecurityUtils;
import com.spzx.order.api.domain.OrderInfo;
import com.spzx.order.api.domain.OrderItem;
import com.spzx.order.domain.vo.OrderTradeVO;
import com.spzx.order.mapper.OrderInfoMapper;
import com.spzx.order.service.IOrderInfoService;
import com.spzx.order.service.IOrderItemService;
import com.spzx.order.service.IOrderLogService;
import com.spzx.product.api.RemoteCartService;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.domain.CartItem;
import com.spzx.product.api.domain.dto.OrderStockDTO;
import com.spzx.product.api.domain.vo.ProductSkuVO;
import com.spzx.user.api.RemoteUserInfoService;
import com.spzx.user.api.domain.UserAddress;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 订单Service业务层处理
 *
 * @author atguigu
 * @date 2024-07-12
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {
    @Resource
    RemoteCartService remoteCartService;
    @Resource
    RemoteProductService remoteProductService;
    @Resource
    RemoteUserInfoService remoteUserInfoService;
    @Resource
    IOrderItemService orderItemService;
    @Resource
    IOrderLogService orderLogService;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    SpzxRabbitmqService spzxRabbitmqService;

    @Autowired
    private OrderInfoMapper orderInfoMapper;
    //去结算
    @Override
    public OrderTradeVO trade() {
        R<List<CartItem>> listR = remoteCartService.getCheckedCartItems();
        if (listR.getCode() != R.SUCCESS) {
            throw new ServiceException("查询购物车数据失败");
        }
        List<OrderItem> orderItemList = listR.getData().stream().map(cartItem -> {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(null);
            orderItem.setSkuId(cartItem.getSkuId());
            orderItem.setSkuName(cartItem.getSkuName());
            orderItem.setThumbImg(cartItem.getThumbImg());
            orderItem.setSkuPrice(cartItem.getSkuPrice());
            orderItem.setSkuNum(cartItem.getSkuNum().longValue());
            return orderItem;
        }).collect(Collectors.toList());

        //创建并设置属性值给 结算页对象
        OrderTradeVO orderTradeVO = new OrderTradeVO();
        orderTradeVO.setOrderItemList(orderItemList);
        orderTradeVO.setTradeNo(IdUtils.fastSimpleUUID());
        BigDecimal totalAmount = orderItemList.stream()
                //获取所有购物项的单个分别计算小计金额 最后累加
                .map(orderItem -> orderItem.getSkuPrice()
                        .multiply(new BigDecimal(orderItem.getSkuNum())))
                .reduce((a, b) -> a.add(b)).get();
        orderTradeVO.setTotalAmount(totalAmount);
        stringRedisTemplate.opsForValue().set("spzx:trade:souce:"+orderTradeVO.getTradeNo() , "1" ,
                60 ,TimeUnit.MINUTES);

//        orderTradeVO.setSource(1);
        return orderTradeVO;
    }
    //提交订单
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String submitOrder(OrderInfo orderInfo) {
        List<OrderItem> orderItems = orderInfo.getOrderItemList();
        if (CollectionUtils.isEmpty(orderItems)) {
            throw new ServiceException("没有选中要购买的商品");
        }
        //0 、幂等性处理： 防止表单重复提交
        //订单编号没有使用 tradeNo ,后续订单提交 到支付加锁使用的orderNo和他没有关系
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("spzx:order:trade:" + orderInfo.getTradeNo()
                , "1", 20, TimeUnit.MINUTES);
        if (!flag) {
            throw new ServiceException("请勿重复提交订单");
        }

//        1、确认订单商品价格 : 商品价格存储在product_sku表中
        // 查询订单中所有商品数据库中的单价 和 订单里面订单项的单价比较
        List<Long> skuIds = orderItems.stream().map(orderItem -> orderItem.getSkuId()).collect(Collectors.toList());
        // 查询  skuId-> price    ， skuId2 -> price2
        R<Map<Long, BigDecimal>> skusPriceR = remoteProductService.getSkusPrice(skuIds);
        if (skusPriceR.getCode() != R.SUCCESS) {
            throw new ServiceException(skusPriceR.getMsg());
        }
        //获取数据库中查询到的sku的价格
        Map<Long, BigDecimal> skusPriceData = skusPriceR.getData();
        orderItems.forEach(orderItem -> {
            //使用提交订单中的价格  和数据库查询到的价格比较
            if (orderItem.getSkuPrice().compareTo(skusPriceData.get(orderItem.getSkuId())) != 0) {
                //同步redis缓存的购物车的价格:
                // 修改购物车商品的价格
                throw new ServiceException("页面数据过期，请刷新");
            }
        });
//        2、确认库存是否足够(验库存锁库存)： 库存存在 sku_stock表中
        // 将 一组skuId 和要它要购买的数量  提交给product服务 去验库存锁库存
        //自定义DTO类： 用来携带验库存锁库存的参数 和响应的信息
        // skuId ： 商品id , skuNum  要购买的数量,  flag 校验是否成功  ,  userId  下单用户
        orderInfo.setUserId(SecurityUtils.getUserId());
        orderInfo.setNickName(SecurityUtils.getUsername());
        orderInfo.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
        //2.1 获取要验库存的 数据集合
        //因为库存 在订单取消/关闭时还需要释放，所以以后订单还需要再次查找锁定的库存
        List<OrderStockDTO> stockDTOS = orderItems.stream().map(orderItem -> {
            OrderStockDTO orderStockDTO = new OrderStockDTO();
            orderStockDTO.setUserId(SecurityUtils.getUserId());
            orderStockDTO.setSkuId(orderItem.getSkuId());
            orderStockDTO.setSkuName(orderItem.getSkuName());
            orderStockDTO.setSkuNum(orderItem.getSkuNum().intValue());
            orderStockDTO.setOrderNo(orderInfo.getOrderNo());
            //给锁定库存的对象 绑定订单编号
            return orderStockDTO;
        }).collect(Collectors.toList());
        //2.2 远程访问验库存接口
        //  库存表：
        //        total_num 总库存 lock_num+available_num,
        //        lock_num 锁定的库存(下单待支付),
        //        available_num 可用库存(用户提交订单时 还剩余多少库存  total_num-lock_num) ,
        //        sale_num  销量
        //       用户提交订单时，锁定库存：  lock_num+ skuNum ， available_num - skuNum
        //       用户取消订单，释放库存：   lock_num-  skuNum ， available_num + skuNum
        //      用户支付成功： total_num - skuNum  lock_num - sku_num
        R<List<OrderStockDTO>> lockStockR = remoteProductService.checkAndLockStock(stockDTOS);
        if (lockStockR.getCode() != R.SUCCESS) {
            throw new ServiceException(lockStockR.getMsg());
        }
        //拼接 库存校验失败的商品信息 抛出异常
        stockDTOS = lockStockR.getData();
        StringBuilder sb = new StringBuilder("");
        boolean checkStock = true;
        for (int i = 0; i < stockDTOS.size(); i++) {
            if (!stockDTOS.get(i).getFlag()) {
                checkStock = false;//库存校验失败
                sb.append(stockDTOS.get(i).getSkuName() + " ");
            }
        }
        //库存校验失败
        if (!checkStock) {
            throw new ServiceException("订单中 " + sb.toString() + " 库存不足");
        }
//        3、创建订单保存
        //订单的收货人地址信息
        R<UserAddress> addressR = remoteUserInfoService.getInfo(orderInfo.getUserAddressId());
        if (addressR.getCode() != R.SUCCESS) {
            throw new ServiceException(addressR.getMsg());
        }



        //运费+商品总金额 - 优惠券金额
        BigDecimal skusAmount = orderItems.stream().map(orderItem -> new BigDecimal(orderItem.getSkuNum()).multiply(orderItem.getSkuPrice()))
                .reduce((a, b) -> a.add(b)).get();
        //订单总金额
        orderInfo.setTotalAmount(orderInfo.getFeightFee().add(skusAmount).subtract(new BigDecimal(0)));
        //优惠券金额
        orderInfo.setCouponAmount(new BigDecimal(0));
        //优惠前价格
        orderInfo.setOriginalTotalAmount(orderInfo.getFeightFee().add(skusAmount));
        //订单状态： 0 待付款
        orderInfo.setOrderStatus(0L);
        //设置地址信息
        orderInfo.setReceiverName(addressR.getData().getName());
        orderInfo.setReceiverPhone(addressR.getData().getPhone());
        orderInfo.setReceiverTagName(addressR.getData().getTagName());
        orderInfo.setReceiverProvince(addressR.getData().getProvinceCode());
        orderInfo.setReceiverCity(addressR.getData().getCityCode());
        orderInfo.setReceiverDistrict(addressR.getData().getDistrictCode());
        orderInfo.setReceiverAddress(addressR.getData().getFullAddress());
        this.save(orderInfo);

        Long orderId = orderInfo.getId();

//        订单：

//        订单项：必须绑定订单id
        orderItems.forEach(orderItem -> {
            orderItem.setOrderId(orderId);
        });
        orderItemService.saveBatch(orderItems);

//        4、删除订单涉及的购物车中选中的购物项： 操作cart服务当前用户的购物车
        //删除购物车的操作：就算失败了 对提交订单的业务影响不大
        // 提交订单的业务 会从  购物车去结算 和 商品详情页立即购买 跳转过来
        // 商品详情页立即购买业务不应该清空购物车
        String source = stringRedisTemplate.opsForValue().get("spzx:trade:souce:" + orderInfo.getTradeNo());
//        if(!StringUtils.isEmpty(source) && source.equals("1")){
////            remoteCartService.delCheckedCartItems();
//
//        }
        //提交订单： 封装其他服务使用消息需要的参数 然后发送消息到mq
        SpzxCorrelationData correlationData = new SpzxCorrelationData();
        correlationData.setExchange(SpzxRabbitmqConstants.ORDER_SUBMIT_EXCHANGE);
        correlationData.setRoutingKey(SpzxRabbitmqConstants.ORDER_SUBMIT_ROUTING_KEY);
        correlationData.setRetry(true);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("userId" , orderInfo.getUserId());
        jsonObject.put("orderId" , orderInfo.getId());
        jsonObject.put("totalAmount",orderInfo.getTotalAmount());
        jsonObject.put("phone" , addressR.getData().getPhone());
        jsonObject.put("source" , source);
        correlationData.setMessage(jsonObject.toJSONString());
        spzxRabbitmqService.sendMsg(correlationData);
        stringRedisTemplate.delete("spzx:trade:souce:" + orderInfo.getTradeNo());
//        R<Boolean> delCartItemsR =
//        if(delCartItemsR.getCode()!=R.SUCCESS){
//            throw new ServiceException(delCartItemsR.getMsg());
//        }
//        5、异步保存订单创建的日志
        orderLogService.saveLogAsync(orderInfo);
        return orderInfo.getId().toString();
    }

    @Override
    public Page<OrderInfo> userOrderInfoList(Integer pageNum, Integer pageSize, Integer orderStatus) {
        Long userId = SecurityUtils.getUserId();
        Page<OrderInfo> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<OrderInfo> queryWrapper = Wrappers.lambdaQuery(OrderInfo.class)
                .eq(OrderInfo::getUserId, userId);
        if(orderStatus!=null){
            queryWrapper.eq(OrderInfo::getOrderStatus, orderStatus);
        }
        queryWrapper.orderByDesc(OrderInfo::getCreateTime);
        page = this.page(page, queryWrapper);

        page.getRecords().forEach(orderInfo -> {
            List<OrderItem> orderItems = orderItemService.list(Wrappers.lambdaQuery(OrderItem.class)
                    .eq(OrderItem::getOrderId, orderInfo.getId()));
            orderInfo.setOrderItemList(orderItems);
        });
        return page;
    }

    @Override
    public OrderInfo selectOrderInfoById(Long orderId) {
//        Long userId = SecurityUtils.getUserId();
        OrderInfo orderInfo = this.getById(orderId);
        List<OrderItem> orderItems = orderItemService.list(Wrappers.lambdaQuery(OrderItem.class)
                .eq(OrderItem::getOrderId, orderInfo.getId()));
        orderInfo.setOrderItemList(orderItems);
        return orderInfo;
    }

    @Override
    public OrderTradeVO buy(Long skuId) {
        //1、查询skuId对应的商品详情
        R<ProductSkuVO> skuVOR = remoteProductService.getSkuDetails(skuId);
        if(skuVOR.getCode() != R.SUCCESS){
            throw new ServiceException(skuVOR.getMsg());
        }
        ProductSkuVO productSkuVO = skuVOR.getData();
        //立即购买 只购买一件商品
        //2、将要购买的sku信息 封装为 订单项集合
        List<OrderItem> orderItemList = new ArrayList<>();
        OrderItem orderItem = new OrderItem();
        orderItem.setSkuId(skuId);
        orderItem.setSkuName(productSkuVO.getProductSku().getSkuName());
        orderItem.setThumbImg(productSkuVO.getProductSku().getThumbImg());
        orderItem.setSkuPrice(productSkuVO.getSkuPrice().getSalePrice());
        orderItem.setSkuNum(1L);
        orderItemList.add(orderItem);
        //3、创建 订单确认页 结算页  需要的数据 对象
        OrderTradeVO orderTradeVO = new OrderTradeVO();
        orderTradeVO.setOrderItemList(orderItemList);
        orderTradeVO.setTradeNo(IdUtils.fastSimpleUUID());
        BigDecimal totalAmount = orderItem.getSkuPrice();
        orderTradeVO.setTotalAmount(totalAmount);
        stringRedisTemplate.opsForValue().set("spzx:trade:souce:"+orderTradeVO.getTradeNo() , "2" , 60 ,TimeUnit.MINUTES);
//        orderTradeVO.setSource(2);
        return orderTradeVO;
    }

    @Override
    public boolean cancelOrder(Long orderId) {
        //和关单业务逻辑一样，调用关闭订单的业务方法 发送关单的mq消息
        closeOrder(orderId.toString());
        return true;
    }

    @Override
    public void closeOrder(String orderId) {
        //1、关单业务: 判断 如果订单的状态为待支付 关闭订单
        //保证幂等性
        //获取锁失败： 直接结束
        try {
            while(!stringRedisTemplate.opsForValue().setIfAbsent("spzx:order:update:lock:" + orderId, "1", 2,
                    TimeUnit.MINUTES)){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //更新订单状态
            OrderInfo orderInfo = this.getById(orderId);
            if(orderInfo.getOrderStatus()!= 0 ){
                return;  //订单状态不是待支付状态
            }
            orderInfo.setOrderStatus(-1L);
            if(this.updateById(orderInfo)){
                //关单成功：
                //更新订单日志的订单状态
                orderLogService.updateStatusAsync(orderId,-1);
                //释放库存： 发送一个mq消息

                SpzxCorrelationData correlationData = new SpzxCorrelationData();
                correlationData.setExchange("spzx.order.exchange");
                correlationData.setRoutingKey("spzx.order.close.success");
                correlationData.setMessage(orderInfo.getOrderNo());
                correlationData.setRetryCount(0);
                correlationData.setRetryLimit(3);
                correlationData.setRetry(true);
                spzxRabbitmqService.sendMsg(correlationData);
            }
        } finally {
            //释放锁
            stringRedisTemplate.delete("spzx:order:update:lock:" + orderId);
        }


    }

    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        OrderInfo orderInfo = this.getOne(Wrappers.lambdaQuery(OrderInfo.class)
                .eq(OrderInfo::getOrderNo , orderNo));
        List<OrderItem> orderItems = orderItemService.list(Wrappers.lambdaQuery(OrderItem.class)
                .eq(OrderItem::getOrderId, orderInfo.getId()));
        orderInfo.setOrderItemList(orderItems);
        return orderInfo;
    }

}
